package com.blog.blog.service.impl;

import com.blog.blog.common.PageResponse;
import com.blog.blog.dto.ArticleListQueryDTO;
import com.blog.blog.mapper.ArticleMapper;
import com.blog.blog.mapper.CommentMapper;
import com.blog.blog.mapper.CategoryMapper;
import com.blog.blog.mapper.TagMapper;
import com.blog.blog.service.oss.OssTrackingService;
import com.blog.blog.service.ArticleService;
import com.blog.blog.service.EmailNotificationService;
import com.blog.blog.mapper.UserMapper;
import com.blog.blog.vo.ArticleListItemVO;
import com.blog.blog.vo.ArticleDetailVO;
import com.blog.blog.vo.CommentVO;
import com.blog.blog.vo.CategoryVO;
import com.blog.blog.vo.TagVO;
import com.blog.blog.vo.CommentTreeVO;
import com.blog.blog.dto.CreateCommentDTO;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class ArticleServiceImpl implements ArticleService {

    private final ArticleMapper articleMapper;
    private final CommentMapper commentMapper;
    private final CategoryMapper categoryMapper;
    private final TagMapper tagMapper;
    private final UserMapper userMapper;

    private final com.blog.blog.service.ViewCountService viewCountService;
    private final com.blog.blog.service.ContentModerationService contentModerationService;
    private final OssTrackingService ossTrackingService;
    private final EmailNotificationService emailNotificationService;

    public ArticleServiceImpl(ArticleMapper articleMapper, CommentMapper commentMapper, CategoryMapper categoryMapper, TagMapper tagMapper, UserMapper userMapper, com.blog.blog.service.ViewCountService viewCountService, com.blog.blog.service.ContentModerationService contentModerationService, OssTrackingService ossTrackingService, EmailNotificationService emailNotificationService) {
        this.articleMapper = articleMapper;
        this.commentMapper = commentMapper;
        this.categoryMapper = categoryMapper;
        this.tagMapper = tagMapper;
        this.userMapper = userMapper;
        this.viewCountService = viewCountService;
        this.contentModerationService = contentModerationService;
        this.ossTrackingService = ossTrackingService;
        this.emailNotificationService = emailNotificationService;
    }

    @Override
    public PageResponse<ArticleListItemVO> pageArticles(ArticleListQueryDTO queryDTO) {
        List<ArticleListItemVO> items = articleMapper.selectByQuery(queryDTO);
        long total = articleMapper.countByQuery(queryDTO);
        return new PageResponse<>(total, items);
    }

    @Override
    public ArticleDetailVO getArticleDetail(Long id) {
        // 改为写入 Redis 缓冲，定时合并写回；容错防止 Redis 异常影响详情页
        try {
            viewCountService.incrementView(id);
        } catch (Exception ignore) {}
        return articleMapper.selectDetailById(id);
    }

    @Override
    public void likeArticle(Long id) {
        articleMapper.increaseLikeCount(id);
    }

    @Override
    public List<CommentVO> listComments(Long articleId) {
        return commentMapper.listByArticleId(articleId);
    }

    @Override
    public void addComment(CreateCommentDTO dto) {
        if (dto.getParentId() == null) {
            dto.setParentId(0L);
        }
        if (dto.getContent() != null) {
            dto.setContent(contentModerationService.sanitize(dto.getContent()));
        }
        
        // 插入评论
        commentMapper.insertComment(dto);
        articleMapper.increaseCommentCount(dto.getArticleId());
        
        // 发送邮件通知
        try {
            // 获取文章信息
            ArticleDetailVO article = articleMapper.selectDetailById(dto.getArticleId());
            if (article != null) {
                // 创建评论VO对象用于邮件通知
                CommentVO commentVO = new CommentVO();
                commentVO.setNickname(dto.getNickname());
                commentVO.setContent(dto.getContent());
                commentVO.setCreateTime(new java.time.LocalDateTime[]{java.time.LocalDateTime.now()}[0]);
                
                if (dto.getParentId() != null && dto.getParentId() > 0) {
                    // 这是回复评论，发送回复通知
                    CommentVO parentComment = commentMapper.selectById(dto.getParentId());
                    if (parentComment != null && parentComment.getEmail() != null && !parentComment.getEmail().isEmpty()) {
                        emailNotificationService.sendReplyNotification(
                            article.getTitle(), 
                            dto.getArticleId(), 
                            commentVO, 
                            parentComment, 
                            parentComment.getEmail()
                        );
                    }
                } else {
                    // 这是新评论，发送评论通知给文章作者
                    if (article.getAuthorId() != null) {
                        String authorEmail = userMapper.selectEmailById(article.getAuthorId());
                        if (authorEmail != null && !authorEmail.isEmpty()) {
                            emailNotificationService.sendCommentNotification(
                                article.getTitle(), 
                                dto.getArticleId(), 
                                commentVO, 
                                authorEmail
                            );
                        }
                    }
                }
            }
        } catch (Exception e) {
            // 邮件发送失败不影响评论功能
            System.err.println("发送评论通知邮件失败: " + e.getMessage());
        }
    }

    @Override
    public PageResponse<CommentTreeVO> pageCommentTrees(Long articleId, Integer pageNo, Integer pageSize) {
        int p = (pageNo == null || pageNo < 1) ? 1 : pageNo;
        int s = (pageSize == null || pageSize < 1) ? 10 : Math.min(pageSize, 100);
        int offset = (p - 1) * s;

        List<CommentVO> tops = commentMapper.listTopComments(articleId, s, offset);
        long total = commentMapper.countTopComments(articleId);
        List<Long> currentLevelIds = tops.stream().map(CommentVO::getId).collect(Collectors.toList());

        Map<Long, CommentTreeVO> idToNode = tops.stream().map(vo -> {
            CommentTreeVO node = new CommentTreeVO();
            node.setId(vo.getId());
            node.setArticleId(vo.getArticleId());
            node.setParentId(vo.getParentId());
            node.setContent(vo.getContent());
            node.setNickname(vo.getNickname());
            node.setEmail(vo.getEmail());
            node.setCreateTime(vo.getCreateTime());
            return node;
        }).collect(Collectors.toMap(CommentTreeVO::getId, n -> n));

        // 递归/迭代查询所有子级并组装树
        while (!currentLevelIds.isEmpty()) {
            List<CommentVO> children = commentMapper.listChildren(articleId, currentLevelIds);
            if (children == null || children.isEmpty()) {
                break;
            }
            List<Long> nextLevelIds = children.stream().map(CommentVO::getId).collect(Collectors.toList());
            for (CommentVO child : children) {
                CommentTreeVO childNode = new CommentTreeVO();
                childNode.setId(child.getId());
                childNode.setArticleId(child.getArticleId());
                childNode.setParentId(child.getParentId());
                childNode.setContent(child.getContent());
                childNode.setNickname(child.getNickname());
                childNode.setEmail(child.getEmail());
                childNode.setCreateTime(child.getCreateTime());
                idToNode.put(childNode.getId(), childNode);

                CommentTreeVO parentNode = idToNode.get(child.getParentId());
                if (parentNode != null) {
                    parentNode.getChildren().add(childNode);
                }
            }
            currentLevelIds = nextLevelIds;
        }

        List<CommentTreeVO> records = tops.stream().map(t -> idToNode.get(t.getId())).collect(Collectors.toList());
        return new PageResponse<>(total, records);
    }

    @Override
    public List<CategoryVO> listCategories() {
        return categoryMapper.listAll();
    }

    @Override
    public List<TagVO> listTags() {
        return tagMapper.listAll();
    }

    @Override
    public List<ArticleListItemVO> listRelatedArticles(Long id, Integer limit) {
        int size = (limit == null || limit < 1) ? 5 : Math.min(limit, 20);
        return articleMapper.selectRelated(id, size);
    }

    @Override
    public List<ArticleListItemVO> listHotArticles(Integer limit, Long categoryId, Long tagId, Integer sinceDays) {
        int size = (limit == null || limit < 1) ? 6 : Math.min(limit, 50);
        Integer days = (sinceDays == null || sinceDays <= 0) ? null : Math.min(sinceDays, 365);
        return articleMapper.selectHot(size, categoryId, tagId, days);
    }

    @Override
    public java.util.List<com.blog.blog.vo.ArticleArchiveItemVO> listArchives(Integer limit) {
        int size = (limit == null || limit < 1) ? 200 : Math.min(limit, 2000);
        return articleMapper.selectArchives(size);
    }

    @Override
    public com.blog.blog.common.PageResponse<com.blog.blog.vo.ArticleArchiveItemVO> pageArchives(Integer pageNo, Integer pageSize) {
        int p = (pageNo == null || pageNo < 1) ? 1 : pageNo;
        int s = (pageSize == null || pageSize < 1) ? 50 : Math.min(pageSize, 500);
        int offset = (p - 1) * s;
        long total = articleMapper.countArchives();
        java.util.List<com.blog.blog.vo.ArticleArchiveItemVO> rec = articleMapper.selectArchivesPage(offset, s);
        return new com.blog.blog.common.PageResponse<>(total, rec);
    }

    @Override
    public com.blog.blog.common.PageResponse<ArticleListItemVO> pageHot(Integer pageNo, Integer pageSize, Long categoryId, Long tagId, Integer sinceDays) {
        int p = (pageNo == null || pageNo < 1) ? 1 : pageNo;
        int s = (pageSize == null || pageSize < 1) ? 12 : Math.min(pageSize, 100);
        int offset = (p - 1) * s;
        Integer days = (sinceDays == null || sinceDays <= 0) ? null : Math.min(sinceDays, 365);
        long total = articleMapper.countHot(categoryId, tagId, days);
        java.util.List<ArticleListItemVO> rec = articleMapper.selectHotPage(categoryId, tagId, days, offset, s);
        return new com.blog.blog.common.PageResponse<>(total, rec);
    }

    @Override
    public java.util.List<ArticleListItemVO> listRecentPublished(Integer limit) {
        int size = (limit == null || limit < 1) ? 6 : Math.min(limit, 50);
        return articleMapper.selectRecentPublished(size);
    }

    @Override
    public Long createArticle(com.blog.blog.dto.SaveArticleDTO dto, Long authorId) {
        com.blog.blog.po.ArticlePO po = new com.blog.blog.po.ArticlePO();
        po.setTitle(dto.getTitle());
        po.setContent(dto.getContent());
        po.setSummary(dto.getSummary());
        po.setCoverImage(dto.getCoverImage());
        po.setCategoryId(dto.getCategoryId());
        po.setAuthorId(authorId);
        po.setStatus(dto.getStatus() == null ? 0 : dto.getStatus());
        int n = articleMapper.insertArticle(po);
        if (n > 0) {
            if (dto.getCoverImage() != null) ossTrackingService.markUsedByUrl(dto.getCoverImage());
            // 标记正文图片为使用中
            Set<String> images = com.blog.blog.util.ContentImageUtils.extractImageUrls(dto.getContent());
            for (String url : images) { ossTrackingService.markUsedByUrl(url); }
            return po.getId();
        }
        return null;
    }

    @Override
    public boolean updateArticle(Long id, com.blog.blog.dto.SaveArticleDTO dto) {
        // 标记旧封面为未使用（若发生变更）
        com.blog.blog.vo.ArticleDetailVO old = articleMapper.selectDetailById(id);
        String oldCover = old == null ? null : old.getCoverImage();

        com.blog.blog.po.ArticlePO po = new com.blog.blog.po.ArticlePO();
        po.setId(id);
        po.setTitle(dto.getTitle());
        po.setContent(dto.getContent());
        po.setSummary(dto.getSummary());
        po.setCoverImage(dto.getCoverImage());
        po.setCategoryId(dto.getCategoryId());
        po.setStatus(dto.getStatus());
        boolean ok = articleMapper.updateArticle(po) > 0;
        if (ok) {
            if (oldCover != null && dto.getCoverImage() != null && !oldCover.equals(dto.getCoverImage())) {
                ossTrackingService.markUnusedByUrl(oldCover);
            }
            if (dto.getCoverImage() != null) ossTrackingService.markUsedByUrl(dto.getCoverImage());
            // 正文图片差异追踪：旧的需标记未使用，新的需标记使用中
            Set<String> oldImages = com.blog.blog.util.ContentImageUtils.extractImageUrls(old == null ? null : old.getContent());
            Set<String> newImages = com.blog.blog.util.ContentImageUtils.extractImageUrls(dto.getContent());
            for (String u : oldImages) { if (!newImages.contains(u)) ossTrackingService.markUnusedByUrl(u); }
            for (String u : newImages) { ossTrackingService.markUsedByUrl(u); }
        }
        if (ok && dto.getTagIds() != null) {
            articleMapper.deleteArticleTags(id);
            for (Long tagId : dto.getTagIds()) {
                if (tagId != null) articleMapper.insertArticleTag(id, tagId);
            }
        }
        return ok;
    }

    @Override
    public boolean deleteArticle(Long id) {
        com.blog.blog.vo.ArticleDetailVO old = articleMapper.selectDetailById(id);
        boolean ok = articleMapper.deleteArticle(id) > 0;
        if (ok && old != null) {
            if (old.getCoverImage() != null) ossTrackingService.markUnusedByUrl(old.getCoverImage());
            Set<String> oldImages = com.blog.blog.util.ContentImageUtils.extractImageUrls(old.getContent());
            for (String u : oldImages) ossTrackingService.markUnusedByUrl(u);
        }
        return ok;
    }
}


